import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
import { Effect } from "effect";

export type ModelIgnoreReason =
  | "common_keyword"
  | "no_tool"
  | "no_text_input"
  | "no_completion"
  | "not_llm"
  | "context_too_small";

export type IgnoredModel = { id: string; reasons: ModelIgnoreReason[] };

export type InputModality = "image" | "text";

export type ModelMetadata = {
  input_modalities?: InputModality[];
};

export type ListModelsResult = {
  models: string[];
  ignored: IgnoredModel[];
  metadata: Record<string, ModelMetadata>;
};

export const DEFAULT_RESULT: ListModelsResult = {
  models: [],
  ignored: [],
  metadata: {},
};
export const REQUEST_TIMEOUT = "5 seconds";

export const commonIgnoreKeywords = [
  "embed",
  "sora",
  "tts",
  "whisper",
  "dall-e",
  "audio",
  "image",
  "computer",
  "robotics",
] as const;

export const fetchJson = (url: string, headers: Record<string, string>) =>
  Effect.tryPromise({
    try: async () => {
      const r = await tauriFetch(url, { method: "GET", headers });
      if (!r.ok) {
        const errorBody = await r.text();
        throw new Error(`HTTP ${r.status}: ${errorBody}`);
      }
      return r.json();
    },
    catch: (e) => e,
  });

export const shouldIgnoreCommonKeywords = (id: string): boolean => {
  const lowerId = id.toLowerCase();
  return commonIgnoreKeywords.some((keyword) => lowerId.includes(keyword));
};

const hasMetadata = (metadata: ModelMetadata | undefined): boolean => {
  if (!metadata) {
    return false;
  }
  if (metadata.input_modalities && metadata.input_modalities.length > 0) {
    return true;
  }
  return false;
};

export const partition = <T,>(
  items: readonly T[],
  shouldIgnore: (item: T) => ModelIgnoreReason[] | null,
  extract: (item: T) => string,
): { models: string[]; ignored: IgnoredModel[] } => {
  const models: string[] = [];
  const ignored: IgnoredModel[] = [];

  for (const item of items) {
    const reasons = shouldIgnore(item);
    const id = extract(item);

    if (!reasons || reasons.length === 0) {
      models.push(id);
    } else {
      ignored.push({ id, reasons });
    }
  }

  return { models, ignored };
};

export const extractMetadataMap = <T,>(
  items: readonly T[],
  extract: (item: T) => string,
  extractMetadata: (item: T) => ModelMetadata | undefined,
): Record<string, ModelMetadata> => {
  const metadata: Record<string, ModelMetadata> = {};

  for (const item of items) {
    const id = extract(item);
    const meta = extractMetadata(item);
    if (hasMetadata(meta)) {
      metadata[id] = meta!;
    }
  }

  return metadata;
};
